package main

import (
	"errors"
	"fmt"
	"log"
)

/**
17_状态模式
概念：对有状态的对象，把复杂的“判断逻辑”提取到不同的状态对象中，允许状态对象在其内部状态发生改变时改变其行为。
*/

/**
例子：假设有一台售卖机只卖一种商品，且只有如下四种状态：
有商品
无商品
商品请求中
已收款
一台售卖机也应该会有多种功能，我们同样做一下简化，假设这台售卖机只有四个功能：
选择商品
补充商品
投币
吐出商品
-------------------------------------------------------------------------------------------
什么时候使用状态模式？
在一个对象有多种不同状态的时候。对象需要根据当前的请求来改变它的状态
在前面提到的例子中，售卖机将会受到行为的影响从一种状态切换到另一种状态。比如，当“投币”行为结束后，售货机将会从“商品请求中”状态切换到“已收款”状态。
在一个对象需要根据它当前的状态对同一个请求做出不同响应的时候。这个时候使用状态模式可以避免大量的条件声明。
仍然以售卖机为例，当用户想购买商品时，如果售卖机的状态为“有商品”，它就会继续处理，如果售卖机状态为“无商品”，它就会拒绝处理。请注意，这里售卖机根据它“有商品”和“无商品”的状态，对购买商品的请求作出了两种不同的响应。
*/

type state interface {
	// 添加商品
	addItem(int) error
	// 请求商品
	requestItem() error
	//	投币
	insertMoney(money int) error
	// 吐出商品
	dispenseItem() error
}

type vendingMachine struct {
	hasItem       state
	itemRequested state
	hasMoney      state
	noItem        state

	currentState state

	itemCount int
	itemPrice int
}

func newVendingMachine(itemCount int, itemPrice int) *vendingMachine {
	v := &vendingMachine{
		itemCount: itemCount,
		itemPrice: itemPrice,
	}
	hasItem := &hasItemState{v}
	itemRequested := &itemRequestedState{v}
	hasMoney := &hasMoneyState{v}
	noItem := &noItemState{v}
	v.setState(hasItem)
	v.hasMoney = hasMoney
	v.hasItem = hasItem
	v.itemRequested = itemRequested
	v.noItem = noItem
	return v
}

func (v *vendingMachine) addItem(i int) error {
	return v.currentState.addItem(i)
}

func (v *vendingMachine) requestItem() error {
	return v.currentState.requestItem()
}

func (v *vendingMachine) insertMoney(money int) error {
	return v.currentState.insertMoney(money)
}

func (v *vendingMachine) dispenseItem() error {
	return v.currentState.dispenseItem()
}
func (v *vendingMachine) incrementItemCount(count int) {
	fmt.Printf("添加%d件商品！ \n", count)
	v.itemCount += count
}

func (v *vendingMachine) setState(s state) {
	v.currentState = s
}

// 选择商品的状态
type hasItemState struct {
	vendingMachine *vendingMachine
}

func (h *hasItemState) addItem(i int) error {
	fmt.Printf("添加%d件商品！ \n", i)
	h.vendingMachine.incrementItemCount(i)
	return nil
}

func (h *hasItemState) requestItem() error {
	if h.vendingMachine.itemCount == 0 {
		h.vendingMachine.setState(h.vendingMachine.noItem)
		return errors.New("无商品")
	}
	fmt.Printf("选择了商品\n")
	h.vendingMachine.setState(h.vendingMachine.itemRequested)
	return nil
}

func (h *hasItemState) insertMoney(money int) error {
	return errors.New("请先选择商品！")
}

func (h *hasItemState) dispenseItem() error {
	return errors.New("请先选择商品！")
}

// 付完钱吐出商品的状态
type hasMoneyState struct {
	vendingMachine *vendingMachine
}

func (h *hasMoneyState) addItem(i int) error {
	return errors.New("正在吐出商品！")
}

func (h *hasMoneyState) requestItem() error {
	return errors.New("正在吐出商品！")
}

func (h *hasMoneyState) insertMoney(money int) error {
	return errors.New("正在吐出商品！")
}

func (h *hasMoneyState) dispenseItem() error {
	fmt.Println("正在吐出商品")
	h.vendingMachine.itemCount -= 1
	if h.vendingMachine.itemCount == 0 {
		h.vendingMachine.setState(h.vendingMachine.noItem)
	} else {
		h.vendingMachine.setState(h.vendingMachine.hasItem)
	}
	return nil
}

// 付钱的阶段
type itemRequestedState struct {
	vendingMachine *vendingMachine
}

func (i *itemRequestedState) addItem(i2 int) error {
	return errors.New("商品正在被选择")
}

func (i *itemRequestedState) requestItem() error {
	return errors.New("商品已被选择")
}

func (i *itemRequestedState) insertMoney(money int) error {
	if money < i.vendingMachine.itemPrice {
		return errors.New("钱不够")
	}
	fmt.Println("付钱了")
	i.vendingMachine.setState(i.vendingMachine.hasMoney)
	return nil
}

func (i *itemRequestedState) dispenseItem() error {
	return errors.New("请先付款")
}

// 没有库存的阶段
type noItemState struct {
	vendingMachine *vendingMachine
}

func (n *noItemState) addItem(i int) error {
	n.vendingMachine.incrementItemCount(i)
	n.vendingMachine.setState(n.vendingMachine.hasItem)
	return nil
}

func (n *noItemState) requestItem() error {
	return errors.New("无商品")
}

func (n *noItemState) insertMoney(money int) error {
	return errors.New("无商品")
}

func (n *noItemState) dispenseItem() error {
	return errors.New("无商品")
}

func main() {
	vendingMachine := newVendingMachine(1, 10)
	err := vendingMachine.requestItem()
	if err != nil {
		log.Fatalf(err.Error())
	}
	err = vendingMachine.insertMoney(10)
	if err != nil {
		log.Fatalf(err.Error())
	}
	err = vendingMachine.dispenseItem()
	if err != nil {
		log.Fatalf(err.Error())
	}

	fmt.Println()
	err = vendingMachine.addItem(2)
	if err != nil {
		log.Fatalf(err.Error())
	}

	fmt.Println()

	err = vendingMachine.requestItem()
	if err != nil {
		log.Fatalf(err.Error())
	}

	err = vendingMachine.insertMoney(10)
	if err != nil {
		log.Fatalf(err.Error())
	}

	err = vendingMachine.dispenseItem()
	if err != nil {
		log.Fatalf(err.Error())
	}
}
